package com.jiaofamily.localad.sdk;

import java.lang.ref.WeakReference;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import android.Manifest.permission;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RelativeLayout;

import com.jiaofamily.localad.sdk.adapters.LocalAdAdapter;
import com.jiaofamily.localad.sdk.config.Config;
import com.jiaofamily.localad.sdk.obj.Extra;
import com.jiaofamily.localad.sdk.obj.Ration;
import com.jiaofamily.localad.sdk.util.LocalAdUtil;

/*
 * Example:
 * LinearLayout header = (LinearLayout) activity.findViewById(R.id.header);
 * RelativeLayout.LayoutParams mLayoutParams = new RelativeLayout.LayoutParams(
 * 		LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
 * 		LocalAdLayout.setDynamicAdjustWeight(true);
 * 		LocalAdLayout.setInterval(30);
 *		LocalAdLayout.setAd(LocalAdUtil.NETWORK_TYPE_ADMOB, true, 20, "a14d8079b31c87e", null, null);
 *		LocalAdLayout.setAd(LocalAdUtil.NETWORK_TYPE_YOUMI, true, 50, "fd9d63fe6236e250", "5e3960ad8df0fc4a", null);
 *		LocalAdLayout.setAd(LocalAdUtil.NETWORK_TYPE_WIYUN, true, 30, "31ce6b71c7431a0f", null, null);
 *		LocalAdLayout adLayout=new LocalAdLayout(activity, "");
 *		if(header!=null) {
 *			header.removeAllViews();//solve ad display problem when return from another activity contains ad
 *			header.addView(adLayout, mLayoutParams);
 *		}
 * if(header!=null)
 *		header.invalidate();
 */
public class LocalAdLayout extends RelativeLayout implements LocalAdStateListener{
	private static final String TAG=LocalAdLayout.class.getSimpleName();
	
	private String key=null;//key to identify the author of the app
	
	public Extra extra=null;
	public Ration activeRation=null;
	private Ration nextRation=null;
	
	private ScheduledExecutorService scheduler=null;
	private boolean isScheduled=false;/*indicate if the scheduler can be run*/
	private boolean hasWindow=false;/*indicate if the layout is visible*/
	
	public WeakReference<Activity> activityReference=null;
	private WeakReference<RelativeLayout> superViewReference=null;
	public LocalAdManager manager=null;
	
	public Handler handler=null;
	private int count_sub_view=0;
	
	public static boolean isDynamicAdjustWeight=false;

	/**
	 * 
	 * @param activity current activity
	 * @param key key to indicate who the app belongs to
	 */
	public LocalAdLayout(Activity activity, String key) {
		super(activity);
		// TODO Auto-generated constructor stub		
		this.activityReference=new WeakReference<Activity>(activity);
		this.superViewReference=new WeakReference<RelativeLayout>(this);
		
		this.key=key;
//		this.hasWindow=true;
		
		this.handler=new Handler();
		
		this.scheduler=Executors.newScheduledThreadPool(1);
		this.isScheduled=true;
		this.scheduler.schedule(new InitRunnable(this, key), 0L, TimeUnit.SECONDS);
		
		setHorizontalScrollBarEnabled(false);
		setVerticalScrollBarEnabled(false);
	}

	/*
	 * set ads update interval in seconds, default is 30s
	 * 15~300s
	 */
	public static void setInterval(long update_interval_in_seconds){
		if(update_interval_in_seconds<15){
			update_interval_in_seconds=15;
		}else if(update_interval_in_seconds>300){
			update_interval_in_seconds=300;
		}
		Config.SCHEDULED_INTERVAL=update_interval_in_seconds;
	}
	
	/*
	 * enable specified adAdapter with an initial percent
	 * network_type - LocalAdUtils.NETWORK_TYPE_ADMOB
	 * enable - true or false
	 * initWeight - 0~100
	 */
	public static void setAd(int network_type, boolean enable, int initWeight, String key, String key2, String key3) {
		String name=null;
		switch(network_type) {
		case LocalAdUtil.NETWORK_TYPE_ADMOB:
			name=Config.RATION_ADMOB_NAME;
			break;
		case LocalAdUtil.NETWORK_TYPE_WIYUN:
			name=Config.RATION_WIYUN_NAME;
			break;
		case LocalAdUtil.NETWORK_TYPE_YOUMI:
			name=Config.RATION_YOUMI_NAME;
			break;
		}
		
		Ration ration=new Ration();
		ration.isEnabled=enable;
		ration.key=key;
		ration.key2=key2;
		ration.key3=key3;
		ration.type=network_type;
		ration.name=name;
		if(initWeight<=0) {
			initWeight=0;
			ration.isEnabled=false;
		}else if(initWeight>100) {
			initWeight=100;
		}
		ration.weight=initWeight;
		Config.listRation.add(ration);
	}
	
//	@Override
//	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//		// TODO Auto-generated method stub
//		int widthSize=View.MeasureSpec.getSize(widthMeasureSpec);
//		int heightSize=View.MeasureSpec.getSize(heightMeasureSpec);
//		
//		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//	}

	@Override
	protected void onWindowVisibilityChanged(int visibility) {
		// TODO Auto-generated method stub
		Log.d(TAG,"onWindowVisibilityChanged");
		if(visibility==View.VISIBLE){
			this.hasWindow=true;
			if(!this.isScheduled){
				/*return from other activity like click ads*/
				this.isScheduled=true;
				if(this.extra!=null){
					rotateThreadedNow();
				}else{
					this.scheduler.schedule(new InitRunnable(this, this.key), 0L, TimeUnit.SECONDS);
				}
			}
		}else{
			this.hasWindow=false;
			this.isScheduled=false;
//			this.scheduler.shutdown();
		}
		super.onWindowVisibilityChanged(visibility);
	}

	public void onReceiveAd() {
		// TODO Auto-generated method stub
		
	}

	public void onRefreshAd() {
		// TODO Auto-generated method stub
		
	}

	public void onFail() {
		// TODO Auto-generated method stub
		
	}

	public void onClick() {
		// TODO Auto-generated method stub
		
	}
	
	public void rotateThreadedNow(){
		if(this.isScheduled){
			this.scheduler.schedule(new RotateAdRunnable(this), 0L, TimeUnit.SECONDS);
		}
	}
	
	public void rotateThreadedDelayed(){
		Log.d(TAG,"Will call rotateAd() in "+this.extra.cycleTime+" seconds");
//		if(this.hasWindow) {
		if(this.isScheduled){
			this.scheduler.schedule(new RotateAdRunnable(this), this.extra.cycleTime, TimeUnit.SECONDS);
		}
	}
	
	public static void setDynamicAdjustWeight(Boolean enable) {
		isDynamicAdjustWeight=enable;
	}
	
	public void rotateAd(){
//		if(!this.hasWindow){
//			this.isScheduled=false;
//			return;
//		}
		Log.i(TAG,"Rotating Ad");
		/*parseRation each time in order to dynamic adjust and get Ration*/
//		if(isDynamicAdjustWeight) {
//			this.manager.parseRation();
//		}
		
		this.nextRation=this.manager.getRation();
		if(this.nextRation!=null && isNetworkAvailable()) {
			this.handler.post(new HandleAdRunnable(this));
		}else {
			/* 
			 * no ration item in this.manager.rationlist
			 * it could be a problem of network connection
			 */
			Log.e(TAG, "Network error, cannot fetch ads.");
			rotateThreadedDelayed();
		}
		
		
	}
	
	private boolean isNetworkAvailable(){
		if (this.activityReference == null) {
			return false;
		}

		Context context = this.activityReference.get().getApplicationContext();
		if (context == null) {
			return false;
		}
		if (context
				.checkCallingOrSelfPermission(permission.ACCESS_NETWORK_STATE) == PackageManager.PERMISSION_GRANTED) {
			ConnectivityManager cm=(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
			NetworkInfo ni=cm.getActiveNetworkInfo();
			if(ni!=null && ni.isAvailable()){
				return true;
			}
		}
		
		return false;
	}
	
	private void handleAd(){
		if(this.nextRation==null){
			Log.e(TAG,"nextRation is null!");
			rotateThreadedDelayed();
			return;
		}
		
		String rationInfo=String.format("Showing ad - name: %s", new Object[]{this.nextRation.name});
		Log.d(TAG,rationInfo);
		try{
			LocalAdAdapter.handle(this,this.nextRation);
		}catch (Throwable t){
			Log.w(TAG,"Caught an exception in adapter:",t);
			rollover();
		}
	}
	
	public void rollover(){
		this.nextRation=this.manager.getRollover();
		this.handler.post(new HandleAdRunnable(this));
	}
	
	private void pushSubView(ViewGroup subView){
		RelativeLayout superView=this.superViewReference.get();
		if(superView==null){
			return;
		}
		/*
		 * To fetch from ad server, there will always time needed
		 * To prevent the blank zone by using removeAllViews, here introduce this method
		 * to buffer old view only after new view has been fetched it will then be removed for memory saving
		 */
		if(Config.COUNT_SDK<=1){
			superView.removeAllViews();
		}else if(count_sub_view==2){
			count_sub_view--;
			superView.removeViewAt(0);
//			superView.removeAllViews();
		}
		
		count_sub_view++;
		
		RelativeLayout.LayoutParams layoutParams=new RelativeLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
		layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
		superView.addView(subView,layoutParams);
		if(superView.getChildCount()==2){
			/*show latest ad in front*/
			superView.getChildAt(1).bringToFront();
		}
		
		this.activeRation=this.nextRation;
		countImpression();
	}
	
	private void countImpression(){
		
	}

	public void rollover_pri(){
		this.nextRation=this.manager.getRollover_pri();
//		this.scheduler.shutdown();
//		while(!this.scheduler.isShutdown());
//		this.handler.post(new HandleAdRunnable(this));
//		this.handler.post(new LocalAdLayout.ViewAdRunnable(this, this.nextRation));
		
	}
    
	private static class InitRunnable implements Runnable{
		private WeakReference<LocalAdLayout> layout=null;
		private String key;
		
		public InitRunnable(LocalAdLayout layout, String key) {
			super();
			// TODO Auto-generated constructor stub
			this.layout=new WeakReference<LocalAdLayout>(layout);
			this.key=key;
		}

		public void run() {
			// TODO Auto-generated method stub
			LocalAdLayout layout=this.layout.get();
			if(layout!=null){
				Activity activity=layout.activityReference.get();
				if(activity==null){
					return;
				}
				
				if(!layout.hasWindow){
					//only when the layout is visible then show ads.
					layout.isScheduled=false;
					return;
				}
				
				if(layout.manager==null){
					layout.manager=new LocalAdManager(new WeakReference<Context>(activity.getApplicationContext()),this.key);
				}

				layout.manager.fetchConfig();//in order to getExtra
				layout.extra=layout.manager.getExtra();
				if(layout.extra==null){//seems not possible to enter here if not fetch config from internet server
					if(layout.isScheduled){
						layout.scheduler.schedule(this, Config.SCHEDULED_INTERVAL, TimeUnit.SECONDS);
					}
				}else{
					layout.rotateAd();
				}
			}
		}
	}
	
	private static class HandleAdRunnable implements Runnable{
		private WeakReference<LocalAdLayout> layout;
		
		public HandleAdRunnable(LocalAdLayout layout) {
			this.layout=new WeakReference<LocalAdLayout>(layout);
		}

		public void run() {
			// TODO Auto-generated method stub
			LocalAdLayout layout=this.layout.get();
			layout.handleAd();
		}
	}
	
	private static class RotateAdRunnable implements Runnable{
		private WeakReference<LocalAdLayout> layout;
		
		public RotateAdRunnable(LocalAdLayout layout) {
			this.layout=new WeakReference<LocalAdLayout>(layout);
		}

		public void run() {
			// TODO Auto-generated method stub
			LocalAdLayout layout=this.layout.get();
			if(layout!=null){
				layout.rotateAd();
			}
		}
	}
	
	public static class ViewAdRunnable implements Runnable{
		private WeakReference<LocalAdLayout> layout=null;
		private ViewGroup nextView=null;
		
		public ViewAdRunnable(LocalAdLayout layout, ViewGroup nextView){
			this.layout=new WeakReference<LocalAdLayout>(layout);
			this.nextView=nextView;
		}

		public void run() {
			// TODO Auto-generated method stub
			LocalAdLayout layout=this.layout.get();
			if(layout!=null){
				layout.pushSubView(this.nextView);
			}
		}
	}
}